{
    let Pool = [];
    let TileWidth = null;
    let TileHeight = null;

    let TextureSet = (() => {
        let Atlases = [];
        let TEXTURE_MAP = ["normal", "hit", "destroy", "destroyani"];
        let FrameData = {};
        
        class TextureSet {
            reset () {
                Atlases.length = 0;
                for(let i in FrameData){
                    delete FrameData[i];
                }
            }
            getTexture(frameid, type) {
                return FrameData[frameid][`${type}frame`];
            }

            create (frames, textures) {
                this.reset();

                for(let i in textures){
                    let bitmap = Laya.loader.getRes(`assets/map/${textures[i]}`).bitmap;

                    if (Laya.Render.isWebGL){
                        bitmap.minFifter=0x2600;
                        bitmap.magFifter=0x2600;
                        bitmap.enableMerageInAtlas=false;
                    }

                    Atlases.push(bitmap);
                }

                for(let i in frames){
                    let _frame = frames[i];
                    FrameData[i] = _frame;

                    for(let j in TEXTURE_MAP){
                        let _frameData = _frame[`${TEXTURE_MAP[j]}frame`];
                        if(!_frameData){continue;}
                        let _frames = _frameData.frames;
                        switch (_frameData.type) {
                            case "static":
                                _frameData.textures = Laya.Texture.create(Atlases[_frames.idx], _frames.x, _frames.y, _frames.w, _frames.h, _frames.ox, _frames.oy);
                                break;
                            case "animation":
                                let _textures = _frameData.textures = [];
                                for(let k = 0; k < _frames.length; k++){
                                    _textures.push(Laya.Texture.create(Atlases[_frames[k].idx], _frames[k].x, _frames[k].y, _frames[k].w, _frames[k].h, _frames[k].ox, _frames[k].oy));
                                }
                                break;
                        }
                    }
                }
            }
        }

        return new TextureSet;
    })();

    class TileTexture extends Laya.Sprite {
        constructor () {
            super();
            
            this.normalData = null;
            this.hitData = null;
            this.destroyData = null;
            this.destroyaniData = null;
            this.curFrameIndex = 0;

            this.data = null;

            this.size(TileWidth, TileHeight);
            this.pivotY = TileHeight;
        }

        loop (data) {
            this.graphics.clear();
            this.graphics.drawTexture(data.textures[this.curFrameIndex], 0, 0, data.frames[this.curFrameIndex].w, data.frames[this.curFrameIndex].h);

            this.curFrameIndex++;
            if(this.curFrameIndex == data.textures.length){
                let delay = 0;
                this.curFrameIndex = 0;

                data.keepframe && this.graphics.clear();

                if(data.delay){
                    if(data.delay instanceof Array){
                        delay = Sail.Utils.getRandom(data.delay[0], data.delay[1]);
                    }else{
                        delay = data.delay;
                    }
                }

                Laya.timer.once(delay, this, this.loop, [data]);
            }else{
                Laya.timer.once(data.interval, this, this.loop, [data]);
            }
        }

        reset (frameid, data, frameData) {
            this.itemid = data.itemid;
            this.frameid = frameid;
            this.pos(data.x * TileWidth, (data.y + 1) * TileHeight);
            this.zOrder = this.y;

            if(data.childs){
                
            }

            this.action("restore");

            return this;
        }

        recover () {
            Laya.timer.clearAll(this);
            this.removeSelf();
            this.graphics.clear();

            this.normalData = null;
            this.hitData = null;
            this.destroyData = null;
            this.destroyAnimationData = null;
            this.data = null;

            Pool.push(this);
        }

        shake (angle) {
            this.originX = this.x;
            this.originY = this.y;
            Laya.timer.once(30, this, function () {
                this.x += 5 * Math.cos(angle * Math.PI / 180);
                this.y += 5 * Math.sin(angle * Math.PI / 180);

                Laya.timer.once(30, this, function () {
                    this.x = this.originX;
                    this.y = this.originY;

                    this.originX = null;
                    this.originY = null;
                });
            });
        }

        action (act, data) {
            let frameDataType = "";
            let frameData = null;
            let destroyaniData = null;
            Laya.timer.clearAll(this);
            switch (act) {
                case "restore":
                    frameDataType = "normal";
                    break;
                case "hit":
                    frameDataType = "hit";
                    break;
                case "destroy":
                    frameDataType = "destroy";
                    destroyaniData = TextureSet.getTexture(this.frameid, "destroyani");
                    break;
                case "shake":
                    this.shake(data);
                    return;
            }
            frameData = TextureSet.getTexture(this.frameid, frameDataType);
            
            if(destroyaniData){
                this.destroyAni(destroyaniData, 0, this.showTexture.bind(this, frameData, act));
            }else{
                this.showTexture(frameData, act);
            }
        }

        event (e) {
            switch (e) {
                case "hide":
                    this.alpha = 0.5;
                    break;
                case "show":
                    this.alpha = 1;
                    break;
            }
        }

        showTexture (data, act) {
            switch (data.type) {
                case "static":
                    this.graphics.clear();
                    this.graphics.drawTexture(data.textures, 0, 0, data.frames.w, data.frames.h);
                    break;
                case "animation":
                    this.loop(data);
                    break;
            }

            if(act == "destroy"){
                this.zOrder -= TileHeight;
            }else{
                this.zOrder = this.y;
            }
        }

        destroyAni (data, index, callback) {
            this.graphics.clear();
            this.graphics.drawTexture(data.textures[index], 0, 0, data.frames[index].w, data.frames[index].h);

            index++;
            if(index == data.textures.length){
                callback && callback();
            }else{
                Laya.timer.once(data.interval, this, this.destroyAni, [data, index, callback]);
            }
        }

        static create (frameid, data, frameData) {
            let tile = Pool.length ? Pool.pop() : new this;

            return tile.reset(frameid, data, frameData);
        }

        static setup (data) {
            let tilesize = data.meta.tilesize;
            let textures = data.meta.textures;
            let frames = data.frames;

            TileWidth = tilesize.w;
            TileHeight = tilesize.h;
            
            TextureSet.create(frames, textures);
        }
    }

    Sail.class(TileTexture, "Com.Room.Map.TileTexture");
}